<?php
function DomArSession() {
	return DomArSession::getCurrent();
}

class DomArSession {
	
	private $dirtyObjects;
	
	private $deletedObjects;
	
	private $newObjects;
	
	private $newIdGroupNames = array();
	
	private $validationErrors = array();
	
	private $validationDone = false;
	
	private static $current;
	
	
	private function __construct() {
		$this->cleanObjects = new SplObjectStorage();
		$this->dirtyObjects = new SplObjectStorage();
		$this->deletedObjects = new SplObjectStorage();
		$this->newObjects = new SplObjectStorage();
	}
	
	public static function getCurrent() {
		if( self::$current === NULL )
			self::newCurrent();
		
		return self::$current;
	}
	
	public static function newCurrent() {
		if( self::$current ) {
			self::$current->destroy();
		}
		
		$className = __CLASS__;
		self::$current = new $className;
	}
	
	public static function setCurrent( DomArSession $session ) {
		self::$current = $session;
	}
	
	
	
	
	public function destroy() {
		
		// auto commit here ?
		
	}
	
	public function __destroy( ) {
		$this->destroy();
	}
	
	
	
	
	
	public function unregister( DomArDomainObject $object ) {
		switch( $object->getStatus() ) {
			case DomArDomainObject::STATUS_NEW:
				$this->newObjects->detach( $object );
				$this->newIdGroupNames[$object->getIdGroupName()]--;
			break;
			case DomArDomainObject::STATUS_DIRTY:
				$this->dirtyObjects->detach( $object );
			break;
			case DomArDomainObject::STATUS_DELETED:
				$this->deletedObjects->detach( $object );
			break;
		}
	}
	
	public function register( DomArDomainObject $object ) {
		switch( $object->getStatus() ) {
			case DomArDomainObject::STATUS_NEW:
				$this->newObjects->attach( $object );
				$this->newIdGroupNames[$object->getIdGroupName()]++;
				$this->validationDone = false;
			break;
			case DomArDomainObject::STATUS_DIRTY:
				$this->dirtyObjects->attach( $object );
				$this->validationDone = false;
			break;
			case DomArDomainObject::STATUS_DELETED:
				$this->deletedObjects->attach( $object );
				$this->validationDone = false;
			break;
		}
		
	}
	
	
	
	
	
	
	public function validate() {
		if( $this->validationDone === true )
			return (bool)$this->errors;
		
		$this->validated = true;
		return $this->validateNew() && $this->validateDirty();
	}
	
	public function validateNew() {
		$result = true;
		foreach( $this->newObjects as $object ) {
			if( $object->validated() === false ) {
				$result = false;
			}
		}
		return $result;
	}
	
	public function validateDirty( ) {
		$result  = true;
		foreach( $this->dirtyObjects as $object ) {
			if( $object->validated() === false ) {
				$result = false;
			}
		}
		return $result;
	}

	public function registerValidationError( DomArValidationError $error ) {
		$this->errors[] = $error;	
	}
	
	public function getValidationErrors() {
		return $this->validationErrors;
	}
	
	public function getValidationMessages( $separator = NULL ) {
		$messages = array();
		foreach( $this->validationErrors as $error ) {
			$messages[] = $error->getMessage();
		}
		
		if( $separator !== NULL ) {
			$messages = implode($separator, $messages);
		}
		
		return $messages;
	}
	
	
	
	public function commit() {
		
		try {
			
			// start transaction
			
			while( empty($this->dirtyObjects) === false or empty($this->newObjects) === false or empty($this->deletedObjects) === false ) {
				
				if( !$this->validate() ) {
					throw new Exception();
				}
				
				$this->insertNew();
				$this->updateDirty();
				$this->deleteDeleted();
				
			}
		
		} catch( Exception $e ) {
			// rollback
			throw $e;
		}
		
		// end transaction
		
		self::newCurrent();
		
		return true;
	}
	
	public function insertNew() {
		if( empty($this->newObjects) === false )
			return;
			
		//  before insert listeners
		
		$newObjects = $this->newObjects;
		$this->newObjects = new SplObjectStorage();
		
		foreach( $this->newIdGroupNames as $keyName => $count ) {
			DomArIdGenerator::acquire( $keyName, $count );
		}
		
		foreach( $newObjects as $object ) {
			$map = $object->getMapper()->insert();
		}
		
		// after insert listeners
		
	}
	
	public function updateDirty() {
		if( empty($this->dirtyObjects) === false )
			return;
			
		// before update listeners
		
		$dirtyObjects = $this->newObjects;
		$this->dirtyObjects = new SplObjectStorage();
		
		foreach( $dirtyObjects as $object ) {
			$map = $object->getMapper()->update();
		}
		
		// after insert listeners
	}
	
	public function deleteDeleted() {
		if( empty($this->deletedObjects) === false )
			return;
			
		// before delete listeners
		
		$deletedObjects = $this->newObjects;
		$this->deletedObjects = new SplObjectStorage();
		
		foreach( $deletedObjects as $object ) {
			$object->setStatus( DomArDomainObject::STATUS_NULL );
			$map = $object->getMapper()->delete();
		}	
		
		// after delete listeners
	}
	
	
}
?>